package org.nf.web.adapter;

import cn.hutool.core.util.ReflectUtil;
import org.nf.web.servlet.HandlerAdapter;
import org.nf.web.servlet.MethodArgumentsResovler;
import org.nf.web.servlet.ServletApi;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ServiceLoader;

/**
 * @Author lzp
 * @Date 2024-05-15
 * 处理Method的请求调用
 */
public class MethodHandlerAdapter implements HandlerAdapter {

    private final static ServiceLoader<MethodArgumentsResovler> LOADER =
            ServiceLoader.load(MethodArgumentsResovler.class);

    @Override
    public boolean supports(Object handler) {
        //如果当前的handler是Method的实例则返归ture
        return handler instanceof Method;
    }

    @Override
    public Object handler(Object handler, ServletApi servletApi) throws Exception {
        //将handler转换成Method对象
        Method method = (Method) handler;
        //解析方法参数
        Object[] params = resolveMethodAruguments(method,servletApi);
        //先获取声明method的类class
        Class<?> clazz = method.getDeclaringClass();
        //反射创建类实例
        Object instance = ReflectUtil.newInstance(clazz);
        //调用方法,并将请求对象和响应对象传给方法
        return method.invoke(instance,params);
    }


    /**
     * 解析方法的参数,并转换为Object[]返回
     * @param method 处理请求的方法
     * @param servletApi 请求对象,参数的内容都是从req中得到的
     * @return 返回解析好的参数内容
     */
    private Object[] resolveMethodAruguments(Method method,ServletApi servletApi){

        //得到方法的所有参数信息
        Parameter[] parameters = method.getParameters();
        //创建一个Object[] ,长度和Parameter[]保持一致,用于存放
        //参数的值
        Object[] values = new Object[parameters.length];
        //循环遍历每一个参数，将每一个参数交给参数解析器去解析
        //直到有一个合适的参数解析器完成解析操作
        for (int i = 0; i < parameters.length; i++) {
            Parameter param = parameters[i];
            for (MethodArgumentsResovler resovler :LOADER){
                if (resovler.suppots(param)){
                    values[i] = resovler.resolve(param,servletApi);
                    break;
                }
            }
        }
        return values;
    }
}
